/*!
 * ClockPicker v{package.version} (http://weareoutman.github.io/clockpicker/)
 * Copyright 2014 Wang Shenwei.
 * Licensed under MIT (https://github.com/weareoutman/clockpicker/blob/gh-pages/LICENSE)
 */
;
(function() {
    var $ = window.jQuery,
        $win = $(window),
        $doc = $(document),
        $body;

    // Can I use inline svg ?
    var svgNS = "http://www.w3.org/2000/svg",
        svgSupported = "SVGAngle" in window &&
        (function() {
            var supported,
                el = document.createElement("div");
            el.innerHTML = "<svg/>";
            supported = (el.firstChild && el.firstChild.namespaceURI) == svgNS;
            el.innerHTML = "";
            return supported;
        })();

    // Can I use transition ?
    var transitionSupported = (function() {
        var style = document.createElement("div").style;
        return "transition" in style ||
            "WebkitTransition" in style ||
            "MozTransition" in style ||
            "msTransition" in style ||
            "OTransition" in style;
    })();

    // Listen touch events in touch screen device, instead of mouse events in desktop.
    var touchSupported = "ontouchstart" in window,
        mousedownEvent = "mousedown" + (touchSupported ? " touchstart" : ""),
        mousemoveEvent = "mousemove.clockpicker" + (touchSupported ? " touchmove.clockpicker" : ""),
        mouseupEvent = "mouseup.clockpicker" + (touchSupported ? " touchend.clockpicker" : "");

    // Vibrate the device if supported
    var vibrate = navigator.vibrate ? "vibrate" : navigator.webkitVibrate ? "webkitVibrate" : null;

    function createSvgElement(name) {
        return document.createElementNS(svgNS, name);
    }

    function leadingZero(num) {
        return (num < 10 ? "0" : "") + num;
    }

    // Get a unique id
    var idCounter = 0;

    function uniqueId(prefix) {
        var id = ++idCounter + "";
        return prefix ? prefix + id : id;
    }

    // Clock size
    var dialRadius = 100,
        outerRadius = 80,
        // innerRadius = 80 on 12 hour clock
        innerRadius = 54,
        tickRadius = 13,
        diameter = dialRadius * 2,
        duration = transitionSupported ? 350 : 1;

    // Popover template
    var tpl = [
        '<div class="popover clockpicker-popover">',
        '<div class="arrow"></div>',
        '<div class="popover-title">',
        '<span class="clockpicker-span-hours text-primary"></span>',
        " : ",
        '<span class="clockpicker-span-minutes"></span>',
        '<span class="clockpicker-span-am-pm"></span>',
        "</div>",
        '<div class="popover-content">',
        '<div class="clockpicker-plate">',
        '<div class="clockpicker-canvas"></div>',
        '<div class="clockpicker-dial clockpicker-hours"></div>',
        '<div class="clockpicker-dial clockpicker-minutes clockpicker-dial-out"></div>',
        "</div>",
        '<span class="clockpicker-am-pm-block">',
        "</span>",
        "</div>",
        "</div>"
    ].join("");

    // ClockPicker
    function ClockPicker(element, options) {
        var popover = $(tpl),
            plate = popover.find(".clockpicker-plate"),
            hoursView = popover.find(".clockpicker-hours"),
            minutesView = popover.find(".clockpicker-minutes"),
            amPmBlock = popover.find(".clockpicker-am-pm-block"),
            isInput = element.prop("tagName") === "INPUT",
            input = isInput ? element : element.find("input"),
            addon = element.find(".input-group-addon"),
            self = this,
            timer;

        this.id = uniqueId("cp");
        this.element = element;
        this.options = options;
        this.isAppended = false;
        this.isShown = false;
        this.currentView = "hours";
        this.isInput = isInput;
        this.input = input;
        this.addon = addon;
        this.popover = popover;
        this.plate = plate;
        this.hoursView = hoursView;
        this.minutesView = minutesView;
        this.amPmBlock = amPmBlock;
        this.spanHours = popover.find(".clockpicker-span-hours");
        this.spanMinutes = popover.find(".clockpicker-span-minutes");
        this.spanAmPm = popover.find(".clockpicker-span-am-pm");
        this.amOrPm = "PM";

        // Setup for for 12 hour clock if option is selected
        if (options.twelvehour) {

            var amPmButtonsTemplate = [
                '<div class="clockpicker-am-pm-block">',
                '<button type="button" class="btn btn-sm btn-default clockpicker-button clockpicker-am-button">',
                "AM</button>",
                '<button type="button" class="btn btn-sm btn-default clockpicker-button clockpicker-pm-button">',
                "PM</button>",
                "</div>"
            ].join("");

            var amPmButtons = $(amPmButtonsTemplate);
            //amPmButtons.appendTo(plate);

            ////Not working b/c they are not shown when this runs
            //$('clockpicker-am-button')
            //    .on("click", function() {
            //        self.amOrPm = "AM";
            //        $('.clockpicker-span-am-pm').empty().append('AM');
            //    });
            //    
            //$('clockpicker-pm-button')
            //    .on("click", function() {
            //         self.amOrPm = "PM";
            //        $('.clockpicker-span-am-pm').empty().append('PM');
            //    });

            $('<button type="button" class="btn btn-sm btn-default clockpicker-button am-button">' + "AM" + "</button>")
                .on("click",
                    function() {
                        self.amOrPm = "AM";
                        $(".clockpicker-span-am-pm").empty().append("AM");
                    }).appendTo(this.amPmBlock);


            $('<button type="button" class="btn btn-sm btn-default clockpicker-button pm-button">' + "PM" + "</button>")
                .on("click",
                    function() {
                        self.amOrPm = "PM";
                        $(".clockpicker-span-am-pm").empty().append("PM");
                    }).appendTo(this.amPmBlock);

        }

        if (! options.autoclose) {
            // If autoclose is not setted, append a button
            $('<button type="button" class="btn btn-sm btn-default btn-block clockpicker-button">' +
                    options.donetext +
                    "</button>")
                .click($.proxy(this.done, this))
                .appendTo(popover);
        }

        // Placement and arrow align - make sure they make sense.
        if ((options.placement === "top" || options.placement === "bottom") &&
            (options.align === "top" || options.align === "bottom")) options.align = "left";
        if ((options.placement === "left" || options.placement === "right") &&
            (options.align === "left" || options.align === "right")) options.align = "top";

        popover.addClass(options.placement);
        popover.addClass("clockpicker-align-" + options.align);

        this.spanHours.click($.proxy(this.toggleView, this, "hours"));
        this.spanMinutes.click($.proxy(this.toggleView, this, "minutes"));

        // Show or toggle
        input.on("focus.clockpicker click.clockpicker", $.proxy(this.show, this));
        addon.on("click.clockpicker", $.proxy(this.toggle, this));

        // Build ticks
        var tickTpl = $('<div class="clockpicker-tick"></div>'),
            i,
            tick,
            radian,
            radius;

        // Hours view
        if (options.twelvehour) {
            for (i = 1; i < 13; i += 1) {
                tick = tickTpl.clone();
                radian = i / 6 * Math.PI;
                radius = outerRadius;
                tick.css("font-size", "120%");
                tick.css({
                    left: dialRadius + Math.sin(radian) * radius - tickRadius,
                    top: dialRadius - Math.cos(radian) * radius - tickRadius
                });
                tick.html(i === 0 ? "00" : i);
                hoursView.append(tick);
                tick.on(mousedownEvent, mousedown);
            }
        } else {
            for (i = 0; i < 24; i += 1) {
                tick = tickTpl.clone();
                radian = i / 6 * Math.PI;
                var inner = i > 0 && i < 13;
                radius = inner ? innerRadius : outerRadius;
                tick.css({
                    left: dialRadius + Math.sin(radian) * radius - tickRadius,
                    top: dialRadius - Math.cos(radian) * radius - tickRadius
                });
                if (inner) {
                    tick.css("font-size", "120%");
                }
                tick.html(i === 0 ? "00" : i);
                hoursView.append(tick);
                tick.on(mousedownEvent, mousedown);
            }
        }

        // Minutes view
        for (i = 0; i < 60; i += 5) {
            tick = tickTpl.clone();
            radian = i / 30 * Math.PI;
            tick.css({
                left: dialRadius + Math.sin(radian) * outerRadius - tickRadius,
                top: dialRadius - Math.cos(radian) * outerRadius - tickRadius
            });
            tick.css("font-size", "120%");
            tick.html(leadingZero(i));
            minutesView.append(tick);
            tick.on(mousedownEvent, mousedown);
        }

        // Clicking on minutes view space
        plate.on(mousedownEvent,
            function(e) {
                if ($(e.target).closest(".clockpicker-tick").length === 0) {
                    mousedown(e, true);
                }
            });

        // Mousedown or touchstart
        function mousedown(e, space) {
            var offset = plate.offset(),
                isTouch = /^touch/.test(e.type),
                x0 = offset.left + dialRadius,
                y0 = offset.top + dialRadius,
                dx = (isTouch ? e.originalEvent.touches[0] : e).pageX - x0,
                dy = (isTouch ? e.originalEvent.touches[0] : e).pageY - y0,
                z = Math.sqrt(dx * dx + dy * dy),
                moved = false;

            // When clicking on minutes view space, check the mouse position
            if (space && (z < outerRadius - tickRadius || z > outerRadius + tickRadius)) {
                return;
            }
            e.preventDefault();

            // Set cursor style of body after 200ms
            var movingTimer = setTimeout(function() {
                    $body.addClass("clockpicker-moving");
                },
                200);

            // Place the canvas to top
            if (svgSupported) {
                plate.append(self.canvas);
            }

            // Clock
            self.setHand(dx, dy, ! space, true);

            // Mousemove on document
            $doc.off(mousemoveEvent).on(mousemoveEvent,
                function(e) {
                    e.preventDefault();
                    var isTouch = /^touch/.test(e.type),
                        x = (isTouch ? e.originalEvent.touches[0] : e).pageX - x0,
                        y = (isTouch ? e.originalEvent.touches[0] : e).pageY - y0;
                    if (! moved && x === dx && y === dy) {
                        // Clicking in chrome on windows will trigger a mousemove event
                        return;
                    }
                    moved = true;
                    self.setHand(x, y, false, true);
                });

            // Mouseup on document
            $doc.off(mouseupEvent).on(mouseupEvent,
                function(e) {
                    $doc.off(mouseupEvent);
                    e.preventDefault();
                    var isTouch = /^touch/.test(e.type),
                        x = (isTouch ? e.originalEvent.changedTouches[0] : e).pageX - x0,
                        y = (isTouch ? e.originalEvent.changedTouches[0] : e).pageY - y0;
                    if ((space || moved) && x === dx && y === dy) {
                        self.setHand(x, y);
                    }
                    if (self.currentView === "hours") {
                        self.toggleView("minutes", duration / 2);
                    } else {
                        if (options.autoclose) {
                            self.minutesView.addClass("clockpicker-dial-out");
                            setTimeout(function() {
                                    self.done();
                                },
                                duration / 2);
                        }
                    }
                    plate.prepend(canvas);

                    // Reset cursor style of body
                    clearTimeout(movingTimer);
                    $body.removeClass("clockpicker-moving");

                    // Unbind mousemove event
                    $doc.off(mousemoveEvent);
                });
        }

        if (svgSupported) {
            // Draw clock hands and others
            var canvas = popover.find(".clockpicker-canvas"),
                svg = createSvgElement("svg");
            svg.setAttribute("class", "clockpicker-svg");
            svg.setAttribute("width", diameter);
            svg.setAttribute("height", diameter);
            var g = createSvgElement("g");
            g.setAttribute("transform", "translate(" + dialRadius + "," + dialRadius + ")");
            var bearing = createSvgElement("circle");
            bearing.setAttribute("class", "clockpicker-canvas-bearing");
            bearing.setAttribute("cx", 0);
            bearing.setAttribute("cy", 0);
            bearing.setAttribute("r", 2);
            var hand = createSvgElement("line");
            hand.setAttribute("x1", 0);
            hand.setAttribute("y1", 0);
            var bg = createSvgElement("circle");
            bg.setAttribute("class", "clockpicker-canvas-bg");
            bg.setAttribute("r", tickRadius);
            var fg = createSvgElement("circle");
            fg.setAttribute("class", "clockpicker-canvas-fg");
            fg.setAttribute("r", 3.5);
            g.appendChild(hand);
            g.appendChild(bg);
            g.appendChild(fg);
            g.appendChild(bearing);
            svg.appendChild(g);
            canvas.append(svg);

            this.hand = hand;
            this.bg = bg;
            this.fg = fg;
            this.bearing = bearing;
            this.g = g;
            this.canvas = canvas;
        }

        raiseCallback(this.options.init);
    }

    function raiseCallback(callbackFunction) {
        if (callbackFunction && typeof callbackFunction === "function") {
            callbackFunction();
        }
    }

    // Default options
    ClockPicker.DEFAULTS = {
        'default': "", // default time, 'now' or '13:14' e.g.
        fromnow: 0, // set default time to * milliseconds from now (using with default = 'now')
        placement: "bottom", // clock popover placement
        align: "left", // popover arrow align
        donetext: "完成", // done button text
        autoclose: false, // auto close when minute is selected
        twelvehour: false, // change to 12 hour AM/PM clock from 24 hour
        vibrate: true // vibrate the device when dragging clock hand
    };

    // Show or hide popover
    ClockPicker.prototype.toggle = function() {
        this[this.isShown ? "hide" : "show"]();
    };

    // Set popover position
    ClockPicker.prototype.locate = function() {
        var element = this.element,
            popover = this.popover,
            offset = element.offset(),
            width = element.outerWidth(),
            height = element.outerHeight(),
            placement = this.options.placement,
            align = this.options.align,
            styles = {},
            self = this;

        popover.show();

        // Place the popover
        switch (placement) {
        case "bottom":
            styles.top = offset.top + height;
            break;
        case "right":
            styles.left = offset.left + width;
            break;
        case "top":
            styles.top = offset.top - popover.outerHeight();
            break;
        case "left":
            styles.left = offset.left - popover.outerWidth();
            break;
        }

        // Align the popover arrow
        switch (align) {
        case "left":
            styles.left = offset.left;
            break;
        case "right":
            styles.left = offset.left + width - popover.outerWidth();
            break;
        case "top":
            styles.top = offset.top;
            break;
        case "bottom":
            styles.top = offset.top + height - popover.outerHeight();
            break;
        }

        popover.css(styles);
    };

    // Show popover
    ClockPicker.prototype.show = function(e) {
        // Not show again
        if (this.isShown) {
            return;
        }

        raiseCallback(this.options.beforeShow);

        var self = this;

        // Initialize
        if (! this.isAppended) {
            // Append popover to body
            $body = $(document.body).append(this.popover);

            // Reset position when resize
            $win.on("resize.clockpicker" + this.id,
                function() {
                    if (self.isShown) {
                        self.locate();
                    }
                });

            this.isAppended = true;
        }

        // Get the time
        var value = ((this.input.prop("value") || this.options["default"] || "") + "").split(":");
        if (value[0] === "now") {
            var now = new Date(+ new Date() + this.options.fromnow);
            value = [
                now.getHours(),
                now.getMinutes()
            ];
        }
        this.hours = + value[0] || 0;
        this.minutes = + value[1] || 0;
        this.spanHours.html(leadingZero(this.hours));
        this.spanMinutes.html(leadingZero(this.minutes));

        // Toggle to hours view
        this.toggleView("hours");

        // Set position
        this.locate();

        this.isShown = true;

        // Hide when clicking or tabbing on any element except the clock, input and addon
        $doc.on("click.clockpicker." + this.id + " focusin.clockpicker." + this.id,
            function(e) {
                var target = $(e.target);
                if (target.closest(self.popover).length === 0 &&
                    target.closest(self.addon).length === 0 &&
                    target.closest(self.input).length === 0) {
                    self.hide();
                }
            });

        // Hide when ESC is pressed
        $doc.on("keyup.clockpicker." + this.id,
            function(e) {
                if (e.keyCode === 27) {
                    self.hide();
                }
            });

        raiseCallback(this.options.afterShow);
    };

    // Hide popover
    ClockPicker.prototype.hide = function() {
        raiseCallback(this.options.beforeHide);

        this.isShown = false;

        // Unbinding events on document
        $doc.off("click.clockpicker." + this.id + " focusin.clockpicker." + this.id);
        $doc.off("keyup.clockpicker." + this.id);

        this.popover.hide();

        raiseCallback(this.options.afterHide);
    };

    // Toggle to hours or minutes view
    ClockPicker.prototype.toggleView = function(view, delay) {
        var raiseAfterHourSelect = false;
        if (view === "minutes" && $(this.hoursView).css("visibility") === "visible") {
            raiseCallback(this.options.beforeHourSelect);
            raiseAfterHourSelect = true;
        }
        var isHours = view === "hours",
            nextView = isHours ? this.hoursView : this.minutesView,
            hideView = isHours ? this.minutesView : this.hoursView;

        this.currentView = view;

        this.spanHours.toggleClass("text-primary", isHours);
        this.spanMinutes.toggleClass("text-primary", ! isHours);

        // Let's make transitions
        hideView.addClass("clockpicker-dial-out");
        nextView.css("visibility", "visible").removeClass("clockpicker-dial-out");

        // Reset clock hand
        this.resetClock(delay);

        // After transitions ended
        clearTimeout(this.toggleViewTimer);
        this.toggleViewTimer = setTimeout(function() {
                hideView.css("visibility", "hidden");
            },
            duration);

        if (raiseAfterHourSelect) {
            raiseCallback(this.options.afterHourSelect);
        }
    };

    // Reset clock hand
    ClockPicker.prototype.resetClock = function(delay) {
        var view = this.currentView,
            value = this[view],
            isHours = view === "hours",
            unit = Math.PI / (isHours ? 6 : 30),
            radian = value * unit,
            radius = isHours && value > 0 && value < 13 ? innerRadius : outerRadius,
            x = Math.sin(radian) * radius,
            y = - Math.cos(radian) * radius,
            self = this;
        if (svgSupported && delay) {
            self.canvas.addClass("clockpicker-canvas-out");
            setTimeout(function() {
                    self.canvas.removeClass("clockpicker-canvas-out");
                    self.setHand(x, y);
                },
                delay);
        } else {
            this.setHand(x, y);
        }
    };

    // Set clock hand to (x, y)
    ClockPicker.prototype.setHand = function(x, y, roundBy5, dragging) {
        var radian = Math.atan2(x, - y),
            isHours = this.currentView === "hours",
            unit = Math.PI / (isHours || roundBy5 ? 6 : 30),
            z = Math.sqrt(x * x + y * y),
            options = this.options,
            inner = isHours && z < (outerRadius + innerRadius) / 2,
            radius = inner ? innerRadius : outerRadius,
            value;

        if (options.twelvehour) {
            radius = outerRadius;
        }

        // Radian should in range [0, 2PI]
        if (radian < 0) {
            radian = Math.PI * 2 + radian;
        }

        // Get the round value
        value = Math.round(radian / unit);

        // Get the round radian
        radian = value * unit;

        // Correct the hours or minutes
        if (options.twelvehour) {
            if (isHours) {
                if (value === 0) {
                    value = 12;
                }
            } else {
                if (roundBy5) {
                    value *= 5;
                }
                if (value === 60) {
                    value = 0;
                }
            }
        } else {
            if (isHours) {
                if (value === 12) {
                    value = 0;
                }
                value = inner ? (value === 0 ? 12 : value) : value === 0 ? 0 : value + 12;
            } else {
                if (roundBy5) {
                    value *= 5;
                }
                if (value === 60) {
                    value = 0;
                }
            }
        }

        // Once hours or minutes changed, vibrate the device
        if (this[this.currentView] !== value) {
            if (vibrate && this.options.vibrate) {
                // Do not vibrate too frequently
                if (! this.vibrateTimer) {
                    navigator[vibrate](10);
                    this.vibrateTimer = setTimeout($.proxy(function() {
                                this.vibrateTimer = null;
                            },
                            this),
                        100);
                }
            }
        }

        this[this.currentView] = value;
        this[isHours ? "spanHours" : "spanMinutes"].html(leadingZero(value));

        // If svg is not supported, just add an active class to the tick
        if (! svgSupported) {
            this[isHours ? "hoursView" : "minutesView"].find(".clockpicker-tick").each(function() {
                var tick = $(this);
                tick.toggleClass("active", value === + tick.html());
            });
            return;
        }

        // Place clock hand at the top when dragging
        if (dragging || (! isHours && value % 5)) {
            this.g.insertBefore(this.hand, this.bearing);
            this.g.insertBefore(this.bg, this.fg);
            this.bg.setAttribute("class", "clockpicker-canvas-bg clockpicker-canvas-bg-trans");
        } else {
            // Or place it at the bottom
            this.g.insertBefore(this.hand, this.bg);
            this.g.insertBefore(this.fg, this.bg);
            this.bg.setAttribute("class", "clockpicker-canvas-bg");
        }

        // Set clock hand and others' position
        var cx = Math.sin(radian) * radius,
            cy = - Math.cos(radian) * radius;
        this.hand.setAttribute("x2", cx);
        this.hand.setAttribute("y2", cy);
        this.bg.setAttribute("cx", cx);
        this.bg.setAttribute("cy", cy);
        this.fg.setAttribute("cx", cx);
        this.fg.setAttribute("cy", cy);
    };

    // Hours and minutes are selected
    ClockPicker.prototype.done = function() {
        raiseCallback(this.options.beforeDone);
        this.hide();
        var last = this.input.prop("value"),
            value = leadingZero(this.hours) + ":" + leadingZero(this.minutes);
        if (this.options.twelvehour) {
            value = value + this.amOrPm;
        }

        this.input.prop("value", value);
        if (value !== last) {
            this.input.triggerHandler("change");
            if (! this.isInput) {
                this.element.trigger("change");
            }
        }

        if (this.options.autoclose) {
            this.input.trigger("blur");
        }

        raiseCallback(this.options.afterDone);
    };

    // Remove clockpicker from input
    ClockPicker.prototype.remove = function() {
        this.element.removeData("clockpicker");
        this.input.off("focus.clockpicker click.clockpicker");
        this.addon.off("click.clockpicker");
        if (this.isShown) {
            this.hide();
        }
        if (this.isAppended) {
            $win.off("resize.clockpicker" + this.id);
            this.popover.remove();
        }
    };

    // Extends $.fn.clockpicker
    $.fn.clockpicker = function(option) {
        var args = Array.prototype.slice.call(arguments, 1);
        return this.each(function() {
            var $this = $(this),
                data = $this.data("clockpicker");
            if (! data) {
                var options = $.extend({}, ClockPicker.DEFAULTS, $this.data(), typeof option == "object" && option);
                $this.data("clockpicker", new ClockPicker($this, options));
            } else {
                // Manual operatsions. show, hide, remove, e.g.
                if (typeof data[option] === "function") {
                    data[option].apply(data, args);
                }
            }
        });
    };
}());
